/* Generator.cs

	Infinite Rogue
	By Dan Alexander, Andy Law 
	July 2012

	Cross platform roguelike using C#, Mono, WinForms and GDI.
	
	LICENSE: New BSD License
*/

using System;

public class Generator
{	
	//Min and max room dimensions
	int roommax = 8;
	int roommin = 4;
	int nroommin = 20;
	int nroommax = 50; 
	//Empty cell flag.
	int emptycell = 0;
	
	//Level size
	int mapw = 100; //map width
	int maph = 100; //map height

	//Grid cell size (width and height)
	int csize = 10;
	//Grid dimensions.
	int gw;
	int gh;

	//Coordinates.
    public struct mcoord
	{
		public int x;
		public int y;
	}

	//Room dimensions.
    public struct rmdim
	{
		public int rwidth;
		public int rheight;
		public mcoord rcoord;
	}

	//Room information
	public struct roomtype
	{
		public rmdim roomdim;  //Room width and height
		public mcoord tl;      //Room rect
		public mcoord br;
	}

	//Grid cell structure.
    public struct celltype
	{
		public mcoord cellcoord; //The cell position.
		public int Room;     //Room id. This is an index into the room array.
	}

	roomtype[] rooms;   //Room array.
	celltype[,] grid; //Grid of cells.
	int numrooms;	//Number of rooms in map.

	public char[,] level; //level
	char floor = '.';
	char wall = '#';
	
	public Generator(int width, int height)
	{
		
		mapw = width;
		maph = height;
		
		nroommax = mapw / 2;
		
		csize = mapw / 10;
		
		gw = mapw / csize;
		gh = maph / csize;
		
		grid = new celltype[gw,gh];
		
		level = new char[mapw,maph];
		
		for (int i = 0; i < maph; ++i)
		{
			for (int j = 0; j < mapw; ++j)
			{
				level[j,i] = wall;
			}
		}
		
		rooms = new roomtype[nroommax];
	}
	
	public Generator(int width, int height, int _maxrooms)
	{
		
		mapw = width;
		maph = height;
		
		nroommax = _maxrooms;
		
		csize = mapw / 10;
		
		gw = mapw / csize;
		gh = maph / csize;
		
		grid = new celltype[gw,gh];
		
		level = new char[mapw,maph];
		
		for (int i = 0; i < maph; ++i)
		{
			for (int j = 0; j < mapw; ++j)
			{
				level[j,i] = wall;
			}
		}
		
		rooms = new roomtype[nroommax];
	}
	
	
	public void Build()
	{
		InitGrid();
		DrawMapToArray();
	}
	
	public Map Map()
	{
		Map map = new Map(mapw,maph,level);
		
		return map;
	}
	
	public int[] GetStairs(){
		//this will return 4 integers in an array,
		//being x1,y1,x2,y2
		int[] coords = new int[4];
		
		//get two random rooms
		int startIndex = Utilities.GlobalRandom.Next(numrooms);
		int endIndex = startIndex;
		while (endIndex == startIndex){
			endIndex = Utilities.GlobalRandom.Next(numrooms);
		}
		
		int startX, startY, endX, endY;
		startX = rooms[startIndex].tl.x + (1 + Utilities.GlobalRandom.Next(rooms[startIndex].roomdim.rwidth-1));
		startY = rooms[startIndex].tl.y + (1 + Utilities.GlobalRandom.Next(rooms[startIndex].roomdim.rheight-1));
		//Console.WriteLine(startX.ToString() + "," + startY.ToString());
		
		endX = rooms[endIndex].tl.x + (1 + Utilities.GlobalRandom.Next(rooms[endIndex].roomdim.rwidth-1));
		endY = rooms[endIndex].tl.y + (1 + Utilities.GlobalRandom.Next(rooms[endIndex].roomdim.rheight-1));
		
		coords[0] = startX;
		coords[1] = startY;
		coords[2] = endX;
		coords[3] = endY;
		
		return coords;
	}
		
	
	private void CleanUp()
	{
		//clean up the array to get rid of unneeded chars
	}
	
	public void Print()
	{
		for (int y = 0; y < maph; ++y)
		{
			for (int x = 0; x < maph; ++x)
			{
				Console.Write(level[x,y]);
			}
			Console.WriteLine();
		}
	}
	
	//Init the grid and room arrays
	private void InitGrid()
	{
		int x, y;
		int gx = 1;
		int gy = 1;

		//Clear room array.	
		//Console.WriteLine("CLEARING ROOM ARRAY");
		for (int i = 0; i < nroommax; ++i)
		{
			rooms[i] = new roomtype();
			rooms[i].roomdim.rwidth = 0;
			rooms[i].roomdim.rheight = 0;
			rooms[i].roomdim.rcoord.x = 0;
			rooms[i].roomdim.rcoord.y = 0;
			rooms[i].tl.x = 0;
			rooms[i].tl.y = 0;
			rooms[i].br.x = 0;
			rooms[i].br.y = 0;
		}
		
		//How many rooms
		numrooms = Utilities.GlobalRandom.Next(nroommin, nroommax);
		
		//Build some rooms
		//Console.WriteLine("Building some rooms");
		for (int i = 0; i < numrooms; ++i)
		{
			rooms[i].roomdim.rwidth = Utilities.GlobalRandom.Next(roommin, roommax);
			rooms[i].roomdim.rheight = Utilities.GlobalRandom.Next(roommin, roommax);
		}
		
		//Clear the grid array
		//Console.WriteLine("Clearing the Grid Array");
		for (int i = 0; i < gw; ++i)
		{
			for (int j = 0; j < gh; ++j)
			{
				grid[i,j].cellcoord.x = gx;
				grid[i,j].cellcoord.y = gy;
				grid[i,j].Room = emptycell;
				gy += csize;
			}
			gy = 1;
			gx += csize;
		}
		//Add rooms to the grid
		//Console.WriteLine("Adding rooms to the grid");
		for(int i = 0; i < numrooms; ++i)
		{
			//Find an empty spot in the grid
			do
			{
				x = Utilities.GlobalRandom.Next(2, gw - 1);
				y = Utilities.GlobalRandom.Next(2, gh - 1);
			}
			while(grid[x,y].Room != emptycell);
				
			//Room center
			rooms[i].roomdim.rcoord.x = grid[x,y].cellcoord.x + (rooms[i].roomdim.rwidth / 2) ;  
			rooms[i].roomdim.rcoord.y = grid[x,y].cellcoord.y + (rooms[i].roomdim.rheight / 2);
			//Set the room rect
			rooms[i].tl.x = grid[x,y].cellcoord.x; 
			rooms[i].tl.y = grid[x,y].cellcoord.y;
			rooms[i].br.x = grid[x,y].cellcoord.x + rooms[i].roomdim.rwidth + 1;
			rooms[i].br.y = grid[x,y].cellcoord.y + rooms[i].roomdim.rheight + 1;
			//Save the room index
			grid[x,y].Room = i;
		}
	}


	//Transfer grid data to map array.
	private void DrawMapToArray()
	{
		int i, x, y; //, pr, rr, rl, ru, kr;

		//Draw the first room to map array
		for ( x = rooms[1].tl.x + 1 ; x < rooms[1].br.x - 1; ++x)
		{
			for (y = rooms[1].tl.y + 1; y < rooms[1].br.y - 1; ++y)
			{
				level[x, y] = floor;
			}
		}
		//Draw the rest of the rooms to the map array and connect them.
		for (i = 2; i < numrooms; ++i)
		{
			for (x = rooms[i].tl.x + 1; x < rooms[i].br.x - 1; ++x)
			{
				for (y = rooms[i].tl.y + 1; y < rooms[i].br.y - 1; ++y)
				{
					level[x, y] = floor;
				}
			}
			ConnectRooms(i, i - 1);
		}
	}

	
	//Connect all the rooms.
	private void ConnectRooms(int r1,int r2)
	{
		int x, y; //idx
		mcoord currcell;
		mcoord lastcell;
		bool wflag = false;
		
		currcell = rooms[r1].roomdim.rcoord;
		lastcell = rooms[r2].roomdim.rcoord;
		
		x = currcell.x;
		
		if (x < lastcell.x)
		{
			wflag = false;
			do
			{
				x += 1;
				if (level[x, currcell.y] == wall)
				wflag = true;
				
				if (level[x, currcell.y] == floor && wflag == true)
				return;
				
				level[x, currcell.y] = floor;
			}
			while(x != lastcell.x);
		}

		if (x > lastcell.x)
		{
			wflag = false;
			do
			{
				x -= 1;
				if (level[x, currcell.y] == wall)
					wflag = false;
				if (level[x, currcell.y] == floor && wflag == true)
					return;
				
				level[x, currcell.y] = floor;
			}
			while( x != lastcell.x);
		}

		y = currcell.y;
		
		if (y < lastcell.y)
		{
			wflag = false;
			do
			{
				y += 1;
				if (level[x, y] == wall)
					wflag = true;
				if (level[x, y] == floor && wflag == true) 
					return;
				level[x, y] = floor;
			}
			while(y != lastcell.y);
		}

		if (y > lastcell.y)
		{
			do
			{
				y -= 1;
				if (level[x, y] == wall)
					wflag = true;
				if (level[x, y] == floor && wflag == true)
					return;
				level[x, y] = floor;
			}
			while (y != lastcell.y);
		}
		
		 
	 }

    public roomtype[] GetRooms() { return rooms; }
}
